package com.homedo.microservice.open.service.gateway.manager.web.gateway.service;

import com.google.common.collect.Lists;
import com.homedo.common.bean.data.enums.ResponseEnum;
import com.homedo.common.bean.vo.Pagination;
import com.homedo.common.bean.vo.context.UserContext;
import com.homedo.common.dao.bean.po.base.BasePo;
import com.homedo.common.exception.HomedoException;
import com.homedo.common.service.BaseService;
import com.homedo.common.util.Detect;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.bean.enums.forzuulfilter.ProgressEnum4ZuulFilter;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.bean.enums.forzuulfilter.StatusEnum4ZuulFilter;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.bean.po.Config;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.bean.po.ConfigRelation;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.bean.po.ZuulFilter;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.bean.po.enhanced.ConfigEnhanced;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.bean.po.enhanced.ZuulFilterEnhanced;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.bean.query.ZuulFilterQuery;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.persistence.dao.ConfigDao;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.persistence.dao.ConfigRelationDao;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.persistence.dao.ZuulFilterDao;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.support.ApplicationNameHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 作者 jgshun
 * @date 创建时间 2018年09月10日 16时28分40秒
 */
@Service
public class ZuulFilterService extends BaseService<ZuulFilter> {
    private static final Logger _LOGGER = LoggerFactory.getLogger(ZuulFilterService.class);

    @Autowired
    private ZuulFilterDao zuulFilterDao;
    @Autowired
    private ConfigService configService;
    @Autowired
    private ConfigDao configDao;
    @Autowired
    private ConfigRelationDao configRelationDao;

    @Override
    public List<ZuulFilterEnhanced> getEnhanceds(List<?> ids, UserContext userContext) {
        List<ZuulFilter> zuulFilters = zuulFilterDao.getListByIds(ids, ZuulFilter.class);

        if (!Detect.notEmpty(zuulFilters)) {
            return null;
        }

        List<ZuulFilterEnhanced> zuulFilterEnhanceds = this.convent2Enhanceds(zuulFilters);
        this.appendZuulFilterDisableConfEnhanced(zuulFilterEnhanceds, userContext);

        return zuulFilterEnhanceds;
    }


    @Override
    public List<ZuulFilterEnhanced> getEnhanceds4Active(List<?> ids, UserContext userContext) {
        List<ZuulFilter> zuulFilters = zuulFilterDao.getListByIdsGreaterThanMark0(ids, ZuulFilter.class);

        if (!Detect.notEmpty(zuulFilters)) {
            return null;
        }

        List<ZuulFilterEnhanced> zuulFilterEnhanceds = this.convent2Enhanceds(zuulFilters);
        this.appendZuulFilterDisableConfEnhanced(zuulFilterEnhanceds, userContext);

        return zuulFilterEnhanceds;
    }


    @Override
    public List<ZuulFilterEnhanced> convent2Enhanceds(List<? extends BasePo> pos) {
        List<ZuulFilter> zuulFilters = (List<ZuulFilter>) pos;
        List<ZuulFilterEnhanced> zuulFilterEnhanceds = Lists.newArrayList();
        zuulFilters.forEach(e -> zuulFilterEnhanceds.add(new ZuulFilterEnhanced(e)));
        return zuulFilterEnhanceds;
    }


    public List<ZuulFilterEnhanced> getEnhanceds4Application(String applicationName, List<ProgressEnum4ZuulFilter> progresses, List<StatusEnum4ZuulFilter> statuses, UserContext userContext) {
        List<ZuulFilter> zuulFilters = zuulFilterDao.getList4Application(applicationName, progresses, statuses);

        if (!Detect.notEmpty(zuulFilters)) {
            return null;
        }
        List<ZuulFilterEnhanced> zuulFilterEnhanceds = this.convent2Enhanceds(zuulFilters);
        this.appendZuulFilterDisableConfEnhanced(zuulFilterEnhanceds, userContext);

        return zuulFilterEnhanceds;
    }

    public List<ZuulFilterEnhanced> appendZuulFilterDisableConfEnhanced(List<ZuulFilterEnhanced> zuulFilterEnhanceds, UserContext userContext) {
        List<String> disablePropertyNames = zuulFilterEnhanceds.stream().map(ZuulFilterEnhanced::getDisablePropertyName).collect(Collectors.toList());

        List<ConfigEnhanced> configEnhanceds = configService.getEnhanceds4Application(ApplicationNameHolder.get(), disablePropertyNames, userContext);

        if (!Detect.notEmpty(configEnhanceds)) {
            return zuulFilterEnhanceds;
        }

        this.appendZuulFilterDisableConfEnhanced(zuulFilterEnhanceds, configEnhanceds, userContext);

        return zuulFilterEnhanceds;
    }

    private void appendZuulFilterDisableConfEnhanced(List<ZuulFilterEnhanced> zuulFilterEnhanceds, List<ConfigEnhanced> configEnhanceds, UserContext userContext) {
        zuulFilterEnhanceds.forEach(e -> {
            this.appendZuulFilterDisableConfEnhanced(e, configEnhanceds, userContext);
        });
    }


    private void appendZuulFilterDisableConfEnhanced(ZuulFilterEnhanced zuulFilterEnhanced, List<ConfigEnhanced> configEnhanceds, UserContext userContext) {
        configEnhanceds.forEach(e -> {
            if (e.getApplicationName().equals(zuulFilterEnhanced.getApplicationName()) && e.getName().equals(zuulFilterEnhanced.getDisablePropertyName())) {
                zuulFilterEnhanced.setZuulFilterDisableConfEnhanced(e);
                return;
            }
        });
    }


    public Pagination<ZuulFilterEnhanced> getPagination(ZuulFilterQuery zuulFilterQuery, Pagination<ZuulFilterEnhanced> pagination, UserContext userContext) {
        Pagination<ZuulFilter> zuulFilterPagination = zuulFilterDao.getPagination(zuulFilterQuery, pagination.copy());

        pagination.setCount(zuulFilterPagination.getCount());
        if (!Detect.notEmpty(zuulFilterPagination.getItems())) {
            return pagination;
        }

        pagination.setItems(this.convent2Enhanceds(zuulFilterPagination.getItems()));

        return pagination;
    }

    public void updateZuulFilter(Integer id, Short status, Short progress, String filterCode, String comment, UserContext userContext) {

        ZuulFilter zuulFilter = zuulFilterDao.getById(id);
        if (zuulFilter == null) {
            throw new HomedoException("[过滤器不存在]");
        }

        boolean hasUpdate = false;
        String oldDisablePropertyName = zuulFilter.getDisablePropertyName();

        short oldStatus = zuulFilter.getStatus().shortValue();

        if (oldStatus == StatusEnum4ZuulFilter.CHECK_ING.getCode()) {
            throw new HomedoException("[正在自检中不可修改]");
        }

        if (status != null && oldStatus != status) {
            if (oldStatus == StatusEnum4ZuulFilter.CHECK_FAIL.getCode()) {
                throw new HomedoException("[不可修改自检失败的状态]");
            }

            StatusEnum4ZuulFilter statusEnum4ZuulFilter = StatusEnum4ZuulFilter.getStatusEnumByCode(status);

            if (statusEnum4ZuulFilter != null) {
                zuulFilter.setStatus(status.intValue());

                this.updateCascadeConfig(statusEnum4ZuulFilter, oldDisablePropertyName);

                hasUpdate = true;
            }
        }

        short oldProgress = zuulFilter.getProgress().shortValue();
        if (progress != null && oldProgress != progress) {
            ProgressEnum4ZuulFilter progressEnum4ZuulFilter = ProgressEnum4ZuulFilter.getProgressEnumByCode(progress);

            if (progressEnum4ZuulFilter != null) {
                zuulFilter.setProgress(progress.intValue());
                hasUpdate = true;
            }
        }

        String oldFilterCode = zuulFilter.getFilterCode();
        if (Detect.notEmpty(filterCode) && !oldFilterCode.equals(filterCode)) {

            this.updateZuulFilter(filterCode, zuulFilter);
            zuulFilter.setRevision(zuulFilter.getRevision() + 1);//每次更新版本自动加1
            zuulFilter.setStatus((int) StatusEnum4ZuulFilter.CHECK_ING.getCode());//过滤器代码有变动 即需要重新自检
            this.updateCascadeConfig(StatusEnum4ZuulFilter.CHECK_ING, oldDisablePropertyName);//关闭旧过滤器配置

            hasUpdate = true;
        }

        String oldComment = zuulFilter.getComment();
        if (Detect.notEmpty(comment) && !oldComment.equals(comment)) {
            zuulFilter.setComment(comment);
            hasUpdate = true;
        }

        String newDisablePropertyName = zuulFilter.getDisablePropertyName();
        if (!newDisablePropertyName.equals(oldDisablePropertyName)) {
            this.deleteCascadeConfig(oldDisablePropertyName);
            this.deleteCascadeConfig(zuulFilter.getId());
            this.addCascadeConfig(newDisablePropertyName, zuulFilter.getFilterName());
        }


        if (hasUpdate) {
            zuulFilter.setUpdateTime(new Date());
            zuulFilterDao.updateByPrimaryKeySelective(zuulFilter);
        }

    }

    public ZuulFilter addZuulFilter(String filterCode, String comment, UserContext userContext) {
        this.checkAdd(filterCode, comment);
        ZuulFilter zuulFilter = new ZuulFilter();

        zuulFilter.setApplicationName(ApplicationNameHolder.get());
        zuulFilter.setStatus((int) StatusEnum4ZuulFilter.CHECK_ING.getCode());
        zuulFilter.setProgress((int) ProgressEnum4ZuulFilter.UNPUBLISHED.getCode());
        zuulFilter.setRevision(1);
        zuulFilter.setCreateTime(new Date());
        zuulFilter.setUpdateTime(new Date());
        zuulFilter.setComment(comment);

        this.updateZuulFilter(filterCode, zuulFilter);

        String disablePropertyName = zuulFilter.getDisablePropertyName();
        this.addCascadeConfig(disablePropertyName, zuulFilter.getFilterName());

        return zuulFilterDao.saveSelective(zuulFilter);
    }

    public void deleteZuulFilter(int id, UserContext userContext) {
        ZuulFilter zuulFilter = zuulFilterDao.getById(id);
        this.deleteCascadeConfig(zuulFilter.getDisablePropertyName());

        this.deleteCascadeConfig(id);

        zuulFilterDao.deleteById(id);
    }

    private void checkAdd(String filterCode, String comment) {
        if (!Detect.notEmpty(filterCode) || !Detect.notEmpty(comment)) {
            throw new HomedoException(ResponseEnum.PARAM_ERROR);
        }
    }

    /**
     * 解析filter源码并自动设置相关属性
     *
     * @param filterCode
     * @param zuulFilter
     */
    private void updateZuulFilter(String filterCode, ZuulFilter zuulFilter) {
        String filterName = filterCode.split("class")[1].trim().split(" ")[0].trim();
        String filterType = filterCode.split("filterType")[1].trim().split("}")[0].split("\"")[1];
        if (!filterType.equals("pre") && !filterType.equals("route") && !filterType.equals("post") && !filterType.equals("error")) {
            throw new HomedoException("[过滤器类型不支持:" + filterType + "]");
        }

        String filterId = ApplicationNameHolder.get() + ":" + filterName + ":" + filterType;
        int filterOrder = Integer.valueOf(filterCode.split("filterOrder")[1].trim().split("}")[0].split("return")[1].trim().split(";")[0]);
        String disablePropertyName = "zuul." + filterName + "." + filterType + ".disable";

        zuulFilter.setFilterName(filterName);
        zuulFilter.setFilterType(filterType);
        zuulFilter.setFilterId(filterId);
        zuulFilter.setFilterOrder(filterOrder);
        zuulFilter.setDisablePropertyName(disablePropertyName);
        zuulFilter.setFilterCode(filterCode);

    }

    private void updateCascadeConfig(StatusEnum4ZuulFilter statusEnum4ZuulFilter, String name) {
        Config config = configDao.getListByApplication(ApplicationNameHolder.get(), name);
        if (config == null) {
            return;
        }

        if (statusEnum4ZuulFilter == StatusEnum4ZuulFilter.EFFECTIVE) {
            config.setValue(Boolean.FALSE.toString());
        } else {
            config.setValue(Boolean.TRUE.toString());
        }

        config.setUpdateTime(new Date());

        configDao.updateByPrimaryKeySelective(config);
    }

    private void addCascadeConfig(String name, String filterName) {
        Config config = new Config();
        config.setName(name);
        config.setValue(Boolean.FALSE.toString());
        config.setCreateTime(new Date());
        config.setUpdateTime(new Date());
        config.setApplicationName(ApplicationNameHolder.get());
        config.setComment("过滤器" + filterName + "禁用属性配置:false不禁用,true禁用");

        configDao.saveSelective(config);
    }

    private void deleteCascadeConfig(int id) {
        ZuulFilter zuulFilter = zuulFilterDao.getById(id);

        List<ConfigRelation> configRelations = configRelationDao.getList(zuulFilter.getApplicationName(), Lists.newArrayList(zuulFilter.getFilterId()));

        if (!Detect.notEmpty(configRelations)) {
            return;
        }

        configRelations.forEach(e -> {
            Config config = new Config();
            config.setApplicationName(e.getApplicationName());
            config.setName(e.getConfigName());

            configDao.delete(config);
            configRelationDao.deleteById(e.getId());
        });

    }

    private void deleteCascadeConfig(String name) {
        Config config = new Config();
        config.setName(name);
        config.setApplicationName(ApplicationNameHolder.get());

        configDao.delete(config);
    }


}
